<template>
  <div>
    <div class="center-pane" :class="isWide ? 'wide' : ''">
      <el-row>
        <el-col :span="viewSpan.left">
          <div class="mat-pane">
            <div class="mat-title-pane">
              <span>
                <nav-title :source="mat.source" :name="mat.title" @click-title="onTitle"></nav-title>
              </span>
              <span class="author">
                <a :href="'/#/people/view/' + mat.author">{{ mat.author }}</a>
              </span>
              <span class="mat-stats">
                <star :star="star"></star>
              </span>
              <span v-if="isCode" class="mat-toolbar">
                <a target="_blank" :href="matLink">
                  <ri :i="codeRepoIcon"></ri>
                </a>
                <a v-if="isProject" :href="readme"><ri i="info" title="README"></ri></a>
                <ri v-if="isDir" :i="showHiddenIcon" @click="toggleShowHidden"></ri>
              </span>
              <span v-if="isPoem" class="mat-toolbar">
                <ri i="brush" @click="showWrite"></ri>
                <ri i="sound" @click="showSound"></ri>
                <ri i="note"></ri>
                <ri i="quote"></ri>
                <ri i="translate"></ri>
              </span>

            </div>
            <div class="mat-content">
              <!-- TODO: 选择具体格式展示内容的逻辑是不是应当放到mat-content中去？-->
              <div v-if="mat.format == MatFormat.Dir || mat.format == MatFormat.Project">
                <files-list :dir="dirPath" :showHidden="isShowHidden" @select="select"></files-list>
              </div>
              <div v-else-if="mat.format == MatFormat.File && getPathExt() != 'md'">
                <code-content :content="mat.content" :lang="codeLang" @select="select"></code-content>
              </div>
              <mat-content v-else :content="mat.content" :format="mat.format" @select="select" ></mat-content>
            </div>
          </div>
        </el-col>
        <el-col :span="viewSpan.right">
          <div class="notes-pane">
            <notes
              :context="context"
              @notes-loaded="notesLoaded"
              @update-context="onUpdateContext"
            ></notes>
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
import { NoteKind, NoteScope, MatKind, MatFormat } from "@/js/constants.js";
import MatContent from "../content/MatContent.vue";
import FilesList from "../content/FilesList.vue";
import CodeContent from "../content/CodeContent.vue";
import Notes from "../notes/Notes.vue";
// import InlineNote from "../notes/InlineNote.vue";
import NavTitle from "./NavTitle.vue";
import Star from "@/tools/Star.vue";

// import Vue from "vue";
import paths from "@/js/paths.js";
// const StarBox = Vue.extend(Star);
// const InlineNoteBox = Vue.extend(InlineNote);

// import {defineComponent} from 'vue'

// RemixIcon图标
// import RemixIcon from '@/tools/RemixIcon'





export default {
  components: {
    MatContent,
    Notes,
    Star,
    FilesList,
    CodeContent,
    NavTitle
  },
  data() {
    return {
      MatKind: MatKind,
      MatFormat: MatFormat,
      isWide: false,
      isShowHidden: false,
      id: this.$route.params.mid,
      mat: {
        title: "",
        content: "",
      },
      myNotes: [],
      star: {},
      context: this.dftContext(),
      selectedText: "",
      activeStep: 0,
      showClock: false,
      writeTimeLimit: 30, // TODO: 这个数值应当根据内容长短而有所变化
      isLarge: false,
    };
  },
  created() {
    this.initData();
    console.log("params: ", JSON.stringify(this.$route.params));
   
  },
  mounted() {
  },
  watch: {
    $route() {
      this.initData();
    },
  },
  computed: {
    dirPath() {
      let mat = this.mat;
      return paths.join(mat.source, mat.title);
    },
    viewSpan() {
      if (this.isWide) {
        return {left: 15, right: 9};
      } else {
        return {left: 12, right: 12};
      }
    },
    codeLang() {
      if (this.mat.title) {
        let t = this.mat.title.split(".");
        return "lang-" + t[t.length - 1];
      } else {
        return "";
      }
    },
    isCode() {
      let codeFormats = [MatFormat.Project, MatFormat.Dir, MatFormat.File];
      return codeFormats.includes(this.mat.format);
    },
    isPoem() {
      return this.mat.kind == MatKind.Poem;
    },
    isProject() {
      return this.mat.format == MatFormat.Project;
    },
    isDir() {
      return this.mat.format == MatFormat.Dir || this.mat.format == MatFormat.Project;
    },
    codeRepoIcon() {
      if (this.mat.source == "github.com") {
        return "github";
      } else {
        return "elink";
      }
    },
    matLink() {
      let link = this.mat.link;
      if (!link) {
        console.log(this.mat.source, this.mat.title)
        let p = this.$route.path.replace('/projects/', '');
        link = "https://" + p;
      }
      return link;

    },
    readme() {
      return "/#" + this.$route.path + "/README.md";
    },
    showHiddenIcon() {
      return this.isShowHidden ? "eye" : "eye-close";
    }
  },
  methods: {
    getPathExt() {
      return paths.getExt(this.$route.path);
    },
    toggleShowHidden() {
      this.isShowHidden = !this.isShowHidden;
    },
    initData() {
      this.context = this.dftContext();
      if (this.$route.meta.iswide) {
        this.isWide = true;
      }
      this.mat.content = "";
      // 获取诗歌内容
      let id = this.$route.params.mid;
      console.log("router:", this.$route)
      if (id) {
        this.id = id;
        this.$axios
          .get("/api/v1/mats/get/" + this.id)
          .then((res) => {
            let mat = res.data;
            if (mat.kind == MatKind.Poem) {
              this.isLarge = true;
            }
            this.mat = mat;
            this.context.mat = mat;
            this.context.title = this.mat.title;
            this.loadMyNotes();
          })
          .catch(e => console.log(e));
      } else {
        if (this.$route.meta.isdir) {
          this.mat.format = MatFormat.Dir;
        } else {
          this.mat.format = MatFormat.Text;
        }
        // 为每句话注册响应事件
        let params = this.$route.params;
        let p = paths.join(params.source, params.organization, params.project, params.pathMatch);
        console.log("file path:", p);
        this.$axios.get("/api/v1/mats/getbypath/" + p).then(res => {
          let mat = res.data;
          console.log('mat:', mat)
          this.mat = res.data;
          if (mat.id) {
            this.context.title = this.mat.title;
            this.context.mat = this.mat;
            this.loadMyNotes();
            this.id = mat.id;
          } else {
            // this.mat.title = "/" + p;
            this.context.title = this.mat.title;
            this.context.mat = this.mat;
          }
          console.log(this.mat)
        }).catch(e => console.log(e));
      }
    },
    addStar() {
      var json = {
        scope: NoteScope.Article,
        mid: Number(this.id),
        value: 1,
      };
      return this.authPost("/api/v1/star/add", json).then((res) => {
        let star = res.data;
        this.star = star;
        this.showClock = false;
      });
    },
    handleClose() {
      this.showClock = false;
    },
    showWrite() {
      if (this.$refs.writeclock) {
        this.$refs.writeclock.reset();
      }
      this.showClock = true;
    },
    onUpdateContext(data) {
      Object.assign(this.context, data);
    },
    loadMyNotes() {
      this.authGet("/api/v1/note/list/mine?mid=" + this.mat.id)
        .then((res) => {
          this.myNotes = res.data;
          this.notesLoaded(this.myNotes);
        })
        .catch((e) => console.log(e));
    },
    dftContext() {
      return {
        kind: NoteKind.Note,
        scope: NoteScope.Article,
        title: this.mat ? this.mat.title : "",
        content: "",
        mat: this.mat || {},
      };
    },
    onTitle() {
      this.context = this.dftContext();
      this.selectedText = "";
      this.clearCurrentSelection();
    },
    select(data) {
      //Object.assign(this.selection, data);
      this.context.el = data.el;
      this.context.content = data.content;
      this.context.epos = data.epos;
      this.context.event = data.event;
      this.context.line = data.line;
      this.context.scope = data.scope;
      this.context.spos = data.spos;

      this.selectedText = data.content;
      this.highlightSelection(this.context);
    },
    // 取消其他句子的高亮，并将当前句子高亮
    highlightSelection(sel) {
      this.clearCurrentSelection();
      if (sel.type == "line") {
        let sent = sel.el;
        sent.classList.add("selected");
      } else if (sel.type == "word") {
        let spos = sel.location.startpos;
        let epos = sel.location.endpos;
        let sent = sel.el;
        // 找到所有涉及到的span
        let curIdx = 0;
        for (let span of sent.children) {
          let text = span.textContent;
          let textLen = text.length;
          if (curIdx >= spos && curIdx + textLen <= epos) {
            span.classList.add("selected");
          }
          curIdx += text.length;
        }
        console.log("selected location:", JSON.stringify(sel.location));
        //this.highlightWord(sent, spos, epos);
      }
    },
    clearCurrentSelection() {
      // 清除所有包含selected类别的span
      let highlighted = this.$el.getElementsByClassName("selected");
      Array.from(highlighted).forEach((span) =>
        span.classList.remove("selected")
      );
    },
    notesLoaded(notes) {
      // 整首诗的标星
      for (let star of notes.filter(
        (n) => n.scope == NoteScope.Article && n.kind == NoteKind.Star
      )) {
        console.log("star:", JSON.stringify(star));
        this.star = star;
      }

      // 标星的行
      let linestars = notes.filter((n) => n.scope == NoteScope.Line);
      linestars.sort((a, b) => a.line - b.line);
      if (linestars.length) this.markLines(linestars);
      // 添加注音
      let wordNotes = notes.filter((n) => n.scope == NoteScope.Word);
      if (wordNotes.length) this.markWords(wordNotes);

      // 更新每句内容的热点统计
      //this.updateStats(notes);
      // 高亮单词级别的笔记
      //let wordNotes = notes.filter(n => n.scope == NoteScope.Word);
      //this.markWordNotes(wordNotes);
    },
    markLines(linestars) {
      let lineSpans = this.$el.getElementsByClassName("line");
      if (lineSpans.length == 0) {
        lineSpans = this.$el.getElementsByClassName("code-line");
      }
      if (lineSpans.length == 0) {
        console.error("error: lineSpans should not be empty");
      }
      for (let star of linestars) {
        console.log("line star:", star)
        let lineSpan = lineSpans[star.line-1]
        if (lineSpan) {
          lineSpan.classList.add("mark");
          // const StarBox = defineComponent({ extends: Star })
          // const div = document.createElement('div')
          // const {vNode, destroy, el } = this.$mountComponent(StarBox)
          // console.log(vNode)
          // console.log(destroy)
          // console.log(el)
          // createApp(StarBox, {star: star}).component("ri", RemixIcon).mount(div)
          // lineSpan.nextSibling.append(div)
          /*
          const inst = new StarBox({
            parent: this,
            propsData: {
              star: { value: star.value },
            },
          });
          lineSpan.nextSibling.append(inst.$mount().$el);
          */
        }
      }
    },
    // 把词语级别的笔记标注到文本中
    markWords(notes) {
      // 按照所在行重新组织笔记
      let lines = new Map();
      for (let n of notes) {
        let ln = n.line;
        if (lines.has(ln)) {
          lines.get(ln).push(n);
        } else {
          lines.set(ln, [n]);
        }
      }
      let lineSpans = this.$el.getElementsByClassName("line");
      for (let i of lines.keys()) {
        let ns = lines.get(i);
        ns.sort((a, b) => a.spos - b.spos);
        for (let lineSpan of Array.from(lineSpans).filter(ln=>Number(ln.getAttribute("linenum")) == i)) {
          this.markWordsInLine(ns, lineSpan);
        }
      }
    },
    // 将同一行的笔记标注到该行文本中
    markWordsInLine(notes, lineSpan) {
      // 相同起始位置的笔记组织到一起
      let words = new Map();
      for (let n of notes) {
        let p = n.spos;
        if (words.has(p)) {
          words.get(p).push(n);
        } else {
          words.set(p, [n]);
        }
      }
      console.log("words:", words);
      let text = lineSpan.textContent;
      let s = 0;
      let rs = [];
      // 注意，同一行的笔记互相之间不能交叉。TODO：支持交叉、嵌套的字词笔记
      for (let p of words.keys()) {
        if (s < p) {
          // 纯字符
          rs.push({ kind: "T", text: text.substr(s, p - s) }); // 文本
        }
        let rnote = { kind: "N" };
        let spos = p;
        let epos = p;
        for (let n of words.get(p)) {
          spos = n.spos;
          epos = n.epos;
          if (n.kind == NoteKind.Phonetics) {
            rnote.pinyin = n.content; // 拼音
          } else if (n.kind == NoteKind.Note) {
            rnote.note = n.content; // 笔记、释义等
          } else if (n.kind == NoteKind.Lecture) {
            rnote.lecture = n.content; // 笔记、释义等
          }
          rnote.text = text.substr(spos, epos - spos);
        }
        rs.push(rnote);
        s = epos;
      }
      console.log("current s: ", s);
      console.log("rs: ", rs);
      if (s < text.length) {
        rs.push({ kind: "T", text: text.substr(s) }); // 将最后的文本补齐
      }
      // 备份原始文本
      lineSpan.origText = text;

      // 清空本行的内容，替换为笔记标注
      lineSpan.innerHTML = "";
      for (let r of rs) {
        if (r.kind == "T") {
          let d = document.createTextNode(r.text);
          lineSpan.appendChild(d);
        } else if (r.kind == "N") {
          /*
          let box = new InlineNoteBox({
            parent: this,
            propsData: {
              word: r,
            },
          });
          lineSpan.appendChild(box.$mount().$el);
        */
        }
      }
    },
    updateStats(notes) {
      let sentCounts = {};
      notes.map((n) => {
        if (n.ptype == 1) {
          let sentid = n.sentid;
          if (sentid in sentCounts) {
            sentCounts[sentid]++;
          } else {
            sentCounts[sentid] = 1;
          }
        }
      });
      let sents = this.$el.getElementsByClassName("sent");
      for (let sent of sents) {
        let sentid = sent.id;
        if (sentid in sentCounts) {
          let stat = sent.nextSibling;
          stat.innerText = sentCounts[sentid];
          stat.style.display = "inline";
        }
      }
    },
    showSound() {
      console.log("show sound");
    },
  },
};
</script>

<style lang="stylus">
.go-back
  position absolute
  font-weight bold
  cursor pointer
  color #333
  border 1px solid #eee
  border-radius 5px
  padding 10px
  left 90px

.center-pane
  text-align left

  .mat-pane
    text-align left
    width 100% -10px
    padding 10px

    .mat-title-pane
      padding-bottom 7px
      border-bottom 1px solid #eee

      .mat-title
        padding 10px 10px 10px 0px
        width 100%
        cursor pointer

        .mat-fav
          margin-right 8px

        span.author
          font-weight normal
          color #666

        a.mat-link
          font-size 1.2em
          cursor pointer
          margin-right 10px

        a.mat-link:hover
          color #409EFF

      .mat-stats
        color #409EFF

      .mat-toolbar
        float right
        margin-right 10px

    .mat-status
      min-height 30px

      & div
        float right
        text-align center

    .mat-content
      font-size 1.2em
      min-height 100px
      max-height 400px

      .line
        width auto
        cursor pointer

        &:hover
          background-color #ECF5FF

    border-right 1px solid #eee

  .notes-pane
    padding 0px 10px 0px 30px
    text-align left

  .mat-toolbar
    font-weight normal
    font-size 1.2em

    a
      color black

    span
      margin-left 10px

    svg
      color #409eff
      cursor pointer
      margin-left 10px
</style>